﻿using System;

using Microsoft.Practices.Composite.Events;
using Microsoft.VisualStudio.TestTools.UnitTesting;

using Matchingo.Common.Constants;
using Matchingo.Common.Types.Extensions;
using Matchingo.Common.Types.Interfaces;
using Matchingo.Common.Types.Types;
using Matchingo.Models.GameLogicProvider;
using Matchingo.Models.ScoreProvider;
using Matchingo.Common.Types.Events;

namespace GameLogicTestFixtureApp
{
    [TestClass]
    public class PlayFourCardGameFixture
    {
        private const string PLAYER_NAME = "SmartyP";

        private IHighScoreProvider _scoreProvider;
        private IMemoryGameLogic _gameLogic;
        private IEventAggregator _eventAggregator;

        // find 2 non-matching cards to flip
        private Guid _uniqueID1 = Guid.Empty;
        private Guid _uniqueID2 = Guid.Empty;
        private Guid _notMatchingID = Guid.Empty;

        // we are going to subscribe to events and keep a count of when they are fired
        private int _cardsMatchedEventCount = 0;
        private int _gameEndedEventCount = 0;

        [TestInitialize]
        public void Setup()
        {
            _eventAggregator = new EventAggregator();
        }

        // [ExpectedException(typeof())]
        [TestMethod]
        public void RunFullTest()
        {
            _scoreProvider = new HighScoreProvider(); // we aren't loading because we want 0 records            
            _eventAggregator = new EventAggregator();

            _gameLogic = new MemoryGameLogic(_scoreProvider, _eventAggregator);
            _gameLogic.Initialize();

			Assert.IsTrue(_scoreProvider.GetHighScoresByCardCount(10, CommonConstants.GAME_SETTINGS_DEFAULT_CARDCOUNT).Count == 0); // there should be no game records

            // setup the game we will play
            MatchGameSettings gameSettings = new MatchGameSettings()
            {
                AIGame = false,
                CardCount = 4,
                PlayerName = PLAYER_NAME
            };

            // start a new game ..
            _gameLogic.StartNewGame(gameSettings);

            // there should be 4 game cards
            Assert.IsTrue(_gameLogic.CurrentGame.CardsSet.Count == 4);

            #region Get the first card, a match for it, and a non-match for it

            if (_gameLogic.CurrentGame.CardsSet[0].MatchingID == _gameLogic.CurrentGame.CardsSet[1].MatchingID)
            {
                _uniqueID1 = _gameLogic.CurrentGame.CardsSet[0].UniqueID;
                _uniqueID2 = _gameLogic.CurrentGame.CardsSet[1].UniqueID;
                _notMatchingID = _gameLogic.CurrentGame.CardsSet[2].UniqueID;
            }
            else if (_gameLogic.CurrentGame.CardsSet[0].MatchingID == _gameLogic.CurrentGame.CardsSet[2].MatchingID)
            {
                _uniqueID1 = _gameLogic.CurrentGame.CardsSet[0].UniqueID;
                _uniqueID2 = _gameLogic.CurrentGame.CardsSet[2].UniqueID;
                _notMatchingID = _gameLogic.CurrentGame.CardsSet[1].UniqueID;
            }
            else if (_gameLogic.CurrentGame.CardsSet[0].MatchingID == _gameLogic.CurrentGame.CardsSet[3].MatchingID)
            {
                _uniqueID1 = _gameLogic.CurrentGame.CardsSet[0].UniqueID;
                _uniqueID2 = _gameLogic.CurrentGame.CardsSet[3].UniqueID;
                _notMatchingID = _gameLogic.CurrentGame.CardsSet[2].UniqueID;
            }

            #endregion

            _gameLogic.CardsMatched += (e, m) => { _cardsMatchedEventCount++; }; // listen for CardsMatched            
            _eventAggregator.GetEvent<GameEndedEvent>().Subscribe(args => _gameEndedEventCount++, true);

            // flip the 1st card
            _gameLogic.Flip(_uniqueID1);

            #region Asserts

            Assert.IsTrue(_cardsMatchedEventCount == 0);
            Assert.IsTrue(_gameEndedEventCount == 0);

            // verify 1 face up, 3 face down, 0 matched, 4 unmatched            
            Assert.IsTrue(_gameLogic.CurrentGame.CardsSet.GetCardsFaceUp().Count == 1);
            Assert.IsTrue(_gameLogic.CurrentGame.CardsSet.GetCardsFaceDown().Count == 3);
            Assert.IsTrue(_gameLogic.CurrentGame.CardsSet.GetCardsMatched().Count == 0);
            Assert.IsTrue(_gameLogic.CurrentGame.CardsSet.GetCardsNotMatched().Count == 4);

            Assert.IsTrue(_gameLogic.CurrentGame.CardsSet.GetCardsFaceUpNotMatched().Count == 1);

            #endregion

            // flip the 2nd card (which will NOT match)
            _gameLogic.Flip(_notMatchingID);

            #region Asserts

            Assert.IsTrue(_cardsMatchedEventCount == 0); // no cards should be matched
            Assert.IsTrue(_gameEndedEventCount == 0); // the game should not end

            // verify 2 face up, 2 face down, 0 matched, 4 unmatched
            Assert.IsTrue(_gameLogic.CurrentGame.CardsSet.GetCardsFaceUp().Count == 2);
            Assert.IsTrue(_gameLogic.CurrentGame.CardsSet.GetCardsFaceDown().Count == 2);
            Assert.IsTrue(_gameLogic.CurrentGame.CardsSet.GetCardsMatched().Count == 0);
            Assert.IsTrue(_gameLogic.CurrentGame.CardsSet.GetCardsNotMatched().Count == 4);

            Assert.IsTrue(_gameLogic.CurrentGame.CardsSet.GetCardsFaceUpNotMatched().Count == 2);
            Assert.IsTrue(_gameLogic.CurrentGame.Score == 0); // no matches have happened, so score should be 0

            #endregion

            // NOTE:: we must flip the 2nd card first as the 1st card is already flipped, and clicking on an 
            //        already flipped card does nothing (add relative test?)

            // flip the matching card
            _gameLogic.Flip(_uniqueID2);

            // flip the 1st card again
            _gameLogic.Flip(_uniqueID1);

            #region Asserts

            // verify proper count of events have been fired
            Assert.IsTrue(_cardsMatchedEventCount == 1);
            Assert.IsTrue(_gameEndedEventCount == 0);

            // verify 2 face down, 2 face up, 2 matched, 2 unmatched
            Assert.IsTrue(_gameLogic.CurrentGame.CardsSet.GetCardsFaceUp().Count == 2);
            Assert.IsTrue(_gameLogic.CurrentGame.CardsSet.GetCardsFaceDown().Count == 2);
            Assert.IsTrue(_gameLogic.CurrentGame.CardsSet.GetCardsMatched().Count == 2);
            Assert.IsTrue(_gameLogic.CurrentGame.CardsSet.GetCardsNotMatched().Count == 2);

            Assert.IsTrue(_gameLogic.CurrentGame.CardsSet.GetCardsFaceUpNotMatched().Count == 0);
            Assert.IsTrue(_gameLogic.CurrentGame.Score > 0); // a match has occurred, so the user should have a score now            

            #endregion
        }
    }
}
